package com.edu.marker.http;

import java.io.IOException;
import java.net.Socket;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpVersion;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HttpContext;

import android.content.Context;

public final class EHttpConfigs {
    final HttpClient mHttpClient; 
    final Context mContext;
    
	private EHttpConfigs (Builder builder) {
		this.mHttpClient = builder.mHttpClient;
		this.mContext = builder.mContext;
	}
	
	public static class Builder {
		private static final int DEFAULT_MAX_RETRY = 3;
		private static final int DEFAULT_MAX_CONNECTIONS = 60;
	    private static final int DEFAULT_SOCKET_TIMEOUT = 20 * 1000;
	    private static final int DEFAULT_SOCKET_BUFFER_SIZE = 8192;
	    
	    private int mMaxConns;
	    private int mSoTimeout;
	    private int mConnectionTimeout;
	    private int mBufferSize;
	    private int mMaxRetry;
	    
	    private String mCharset;
	    
	    private HttpParams mHttpParams;
	    private SchemeRegistry mSchemeRegistry;
	    private HttpClient mHttpClient; 
	    private ClientConnectionManager mManager;
	    private HttpRequestRetryHandler mRetryHandler;
	    
		private Context mContext;
		

		public Builder(Context context) {
			this.mContext = context;
			initDefaultValues();
		}
		
		public Builder setMaxConns(int maxConns) {
			this.mMaxConns = maxConns;
			return this;
		}
		
		public Builder setBufferSize(int bufferSize) {
			this.mBufferSize = bufferSize;
			return this;
		}
		
		public Builder setSoTimeout(int timeout) {
			this.mSoTimeout = timeout;
			return this;
		}
		
		public Builder setConnectionTimeout(int timeout) {
			this.mConnectionTimeout = timeout;
			return this;
		}
		
		public Builder setMaxRetry(int maxRetry) {
			this.mMaxRetry = maxRetry;
			return this;
		}
		
		public Builder setCharset(String charset) {
			this.mCharset = charset;
			return this;
		}
		
		public Builder setGZIPEnable(boolean gzipEnable) {
			if(gzipEnable) {
				this.mHttpClient = new GZIPHttpClient(mManager, this.mHttpParams);
				((GZIPHttpClient) this.mHttpClient).setHttpRequestRetryHandler(mRetryHandler);  
			}
			return this;
		}
		
		public EHttpConfigs build() {
			return new EHttpConfigs(this);
		}
		
		private void initDefaultValues() {
			this.mMaxConns = DEFAULT_MAX_CONNECTIONS;
			this.mSoTimeout = DEFAULT_SOCKET_TIMEOUT;
			this.mConnectionTimeout = DEFAULT_SOCKET_TIMEOUT;
			this.mBufferSize = DEFAULT_SOCKET_BUFFER_SIZE;
			this.mMaxRetry = DEFAULT_MAX_RETRY;
			this.mCharset = "UTF-8";
			
			mRetryHandler = new HttpRequestRetryHandler() {

	            @Override
	            public boolean retryRequest(IOException arg0, int arg1, HttpContext arg2) {
	                // retry a max of 5 times
	                if (arg1 >= Builder.this.mMaxRetry) {
	                    return false;
	                }
	                if (arg0 instanceof NoHttpResponseException) {
	                    return true;
	                } else if (arg0 instanceof ClientProtocolException) {
	                    return true;
	                }
	                return false;
	            }
	        };
		
			if(this.mHttpParams == null) {
				this.mHttpParams = new BasicHttpParams();
			}
			
			ConnManagerParams.setTimeout(this.mHttpParams, 1000);
	        ConnManagerParams.setMaxConnectionsPerRoute(this.mHttpParams, new ConnPerRouteBean(20));
	        ConnManagerParams.setMaxTotalConnections(this.mHttpParams, this.mMaxConns);

	        HttpProtocolParams.setVersion(this.mHttpParams, HttpVersion.HTTP_1_1);
	        HttpProtocolParams.setContentCharset(this.mHttpParams, this.mCharset);
	        HttpProtocolParams.setUseExpectContinue(this.mHttpParams, false);
	        
	        HttpConnectionParams.setStaleCheckingEnabled(this.mHttpParams, false);
	        HttpConnectionParams.setSoTimeout(this.mHttpParams, this.mSoTimeout);
	        HttpConnectionParams.setConnectionTimeout(this.mHttpParams, this.mConnectionTimeout);
	        HttpConnectionParams.setTcpNoDelay(this.mHttpParams, true);
	        HttpConnectionParams.setSocketBufferSize(this.mHttpParams, this.mBufferSize);
	        
	        HttpClientParams.setRedirecting(this.mHttpParams, false);
	        
	        if(this.mSchemeRegistry == null) {
	        	this.mSchemeRegistry = new SchemeRegistry();
	        	this.mSchemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
	  	        try {
	  	            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
	  	            trustStore.load(null, null);
	  	            SSLSocketFactory sslfactory = new MySSLSocketFactory(trustStore);
	  	            sslfactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
	  	            this.mSchemeRegistry.register(new Scheme("https", sslfactory, 443));
	  	        } catch (Exception ex) {
	  	            // do nothing, just keep not crash
	  	        }
	        }

	        mManager = new ThreadSafeClientConnManager(this.mHttpParams, this.mSchemeRegistry);
	        if(this.mHttpClient == null) {
	        	this.mHttpClient = new DefaultHttpClient(mManager, this.mHttpParams);
	        	((DefaultHttpClient) this.mHttpClient).setHttpRequestRetryHandler(mRetryHandler);  
	        }
		}
	}
	
	private static class MySSLSocketFactory extends SSLSocketFactory {
        SSLContext sslContext = SSLContext.getInstance("TLS");

        public MySSLSocketFactory(KeyStore truststore) throws Exception {
            super(truststore);

            TrustManager tm = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType)throws CertificateException {}

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException {}

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };

            sslContext.init(null, new TrustManager[] {tm}, null);
        }

        @Override
        public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
        }

        @Override
        public Socket createSocket() throws IOException {
            return sslContext.getSocketFactory().createSocket();
        }
    }
}
